// Copyright (c) 2008-2010, Arne Claus
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 
// following conditions are met:
//
// - Redistributions of source code must retain the above copyright notice, this list of conditions and the following 
//   disclaimer.
// - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
//   disclaimer in the documentation and/or other materials provided with the distribution.
// - Neither the name Arne Claus nor the names of any contributors may be used to endorse or promote products
//   derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef __INCLUDED_CBL_MATH_H__
#define __INCLUDED_CBL_MATH_H__

#include "CBL_Defines.h"
#include <cmath>

CB_SUBNAMESPACE_START(Math)

const float PI = 3.141592653589793f;
const float PIx2 = PI * 2.0f;
const float PI2 = PI / 2.0f;
const float PI180 = PI / 180.0f;

// -------------------------------------------------------------------------------------------------

template<typename TValueType> 
CB_FORCEINLINE TValueType CB_INLINE_ATTR Sqr(const TValueType& _rValue)
{
	return _rValue * _rValue;
}

// -------------------------------------------------------------------------------------------------

template<typename TValueType> 
CB_FORCEINLINE TValueType CB_INLINE_ATTR Sqrt(const TValueType& _rValue)
{
	return sqrt(_rValue);
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE float CB_INLINE_ATTR FastSqrt(float _Value)
{
	static const float ThreeHalfs = 1.5f;
	float HalfValue = _Value * 0.5f;
	
	uint32 FirstGuess = *(uint32*)&_Value;
	FirstGuess = 0x5F375A86 & (FirstGuess >> 1);
	float Result = *(float*)&FirstGuess;
	
	Result *= ThreeHalfs - (HalfValue * Result * Result);
	
	return Result * _Value;
}

// -------------------------------------------------------------------------------------------------

template<typename TValueType> 
CB_FORCEINLINE TValueType CB_INLINE_ATTR Floor(const TValueType& _rValue)
{
	return Floor(_rValue);
}

// -------------------------------------------------------------------------------------------------

template<typename TValueType1, typename TValueType2> 
CB_FORCEINLINE TValueType1 CB_INLINE_ATTR Min(const TValueType1& _rValue1, const TValueType2& _rValue2)
{
	return (_rValue1 < _rValue2) ? _rValue1 : _rValue2;
}

// -------------------------------------------------------------------------------------------------

template<typename TValueType1, typename TValueType2> 
CB_FORCEINLINE TValueType1 CB_INLINE_ATTR Max(const TValueType1& _rValue1, const TValueType2& _rValue2)
{
	return (_rValue1 > _rValue2) ? _rValue1 : _rValue2;
}

// -------------------------------------------------------------------------------------------------

template<typename TValueType1, typename TValueType2> 
CB_FORCEINLINE TValueType1 CB_INLINE_ATTR Lerp(const TValueType1& _rValue1, const TValueType2& _rValue2, float _Scale)
{
	return static_cast<TValueType1>(_Scale * (_rValue1 + _rValue2) - _rValue2);
}

// -------------------------------------------------------------------------------------------------

template<typename TValueType1, typename TValueType2, typename TValueType3> 
CB_FORCEINLINE TValueType1 CB_INLINE_ATTR Clamp(TValueType1 _Value, TValueType2 _Min, TValueType3 _Max)
{
    if (_Value < _Min) return _Min;
    if (_Value > _Max) return _Max;
    return _Value;
}

// -------------------------------------------------------------------------------------------------

template<typename TValueType> 
CB_FORCEINLINE TValueType CB_INLINE_ATTR Abs(TValueType _Value)
{
    if (_Value < 0) return -_Value; 
    return _Value;
}

// -------------------------------------------------------------------------------------------------

template<typename TValueType>
CB_FORCEINLINE float CB_INLINE_ATTR Round(TValueType _Number)
{
    return Floor(_Number + TValueType(0.5f));
}

// -------------------------------------------------------------------------------------------------

template <typename TValueType>
CB_FORCEINLINE TValueType CB_INLINE_ATTR Saturate(TValueType _Value)
{
	return Clamp(_Value, TValueType(0), TValueType(1));
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE float CB_INLINE_ATTR TruncFloat(float _Value, int _DecimalPlace)
{
    float Extend = pow(10.0f, (float)_DecimalPlace);
    int Tmp = (int)(_Value * Extend);
    return Tmp / Extend;
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE float CB_INLINE_ATTR Rad(float _Deg)
{
	return _Deg * PI180;
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE float CB_INLINE_ATTR Deg(float _Rad)
{
	return _Rad / PI180;
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE bool CB_INLINE_ATTR IsPowerOf2(uint32 _Number)
{
	return ((_Number-1) & _Number) == 0;
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE bool CB_INLINE_ATTR IsPowerOf2(uint64 _Number)
{
	return ((_Number-1) & _Number) == 0;
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE uint32 CB_INLINE_ATTR NextPowerOf2(uint32 _Number)
{
	if (IsPowerOf2(_Number)) return _Number;
	--_Number;
	_Number |= _Number >> 8;
	_Number |= _Number >> 4;
	_Number |= _Number >> 2;
	_Number |= _Number >> 1;
	++_Number;
	return _Number;
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE uint64 CB_INLINE_ATTR NextPowerOf2(uint64 _Number)
{
	if (IsPowerOf2(_Number)) return _Number;
	--_Number;
	_Number |= _Number >> 16;
	_Number |= _Number >> 8;
	_Number |= _Number >> 4;
	_Number |= _Number >> 2;
	_Number |= _Number >> 1;
	++_Number;
	return _Number;
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE uint32 CB_INLINE_ATTR PrevPowerOf2(uint32 _Number)
{
    return NextPowerOf2(_Number) >> 1;
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE uint64 CB_INLINE_ATTR PrevPowerOf2(uint64 _Number)
{
    return NextPowerOf2(_Number) >> 1;
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE bool CB_INLINE_ATTR AlmostEqual(float _NumberA, float _NumberB, float _Epsilon=0.00001f)
{
    return fabsf(_NumberA - _NumberB) < _Epsilon;
}

// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE bool CB_INLINE_ATTR AlmostEqual(double _NumberA, double _NumberB, double _Epsilon=0.0000001)
{
    return fabs(_NumberA - _NumberB) < _Epsilon;
}

CB_SUBNAMESPACE_END

#endif